#pragma once
#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem> // C++17, 需要⾼版本编译器和-std=c++17
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
namespace LogModule
{
    std::string sep = "\r\n";
    using namespace MutexMoudle;
    // 定义日志的刷新方式
    class LogStrategy
    {
    public:
        virtual ~LogStrategy() = default;                     // 策略的构造函数
        virtual void SyncLog(const std::string &message) = 0; // 不同模式核⼼是刷
    };
    // 显示器刷新
    class ScreenStrtegy : public LogStrategy
    {
    public:
        ~ScreenStrtegy() {};
        void SyncLog(const std::string &message) override
        {
            LockGuard lock(_mutex);
            std::cout << message << sep;
        }

    private:
        Mutex _mutex;
    };
    const std::string defaultpath = "/var/log/";
    const std::string defaultfile = "log.log";
    class FileLogStrategy : public LogStrategy
    {
    public:
        FileLogStrategy(std::string path = defaultpath, std::string file = defaultfile)
            : _path(path), _file(file)
        {
            LockGuard lock(_mutex);
            // 先构建我们的路径
            if (std::filesystem::exists(_path))
                return;
            try
            {
                std::filesystem::create_directories(_path);
            }
            catch (const std::filesystem::filesystem_error &e)
            {
                std::cerr << e.what() << '\n';
            }
        }
        ~FileLogStrategy() {}
        void SyncLog(const std::string &message) override
        {
            LockGuard lock(_mutex);
            std::string drc = _path + (_path.back() == '/' ? "" : "/") + _file;
            std::ofstream out(drc.c_str(), std::ios::app); // 追加⽅式
            if (!out.is_open())
                return;
            out << message << sep;
            out.close();
        }

    private:
        std::string _path;
        std::string _file;
        Mutex _mutex;
    };

    // ⽇志等级
    enum class LogLevel
    {
        DEBUG,
        INFO,
        WARNING,
        ERROR,
        FATAL
    };
    // ⽇志转换成为字符串
    std::string LogLevelToString(LogLevel level)
    {
        switch (level)
        {
        case LogLevel::DEBUG:
            return "DEBUG";
        case LogLevel::INFO:
            return "INFO";
        case LogLevel::WARNING:
            return "WARNING";
        case LogLevel::ERROR:
            return "ERROR";
        case LogLevel::FATAL:
            return "FATAL";
        default:
            return "UNKNOWN";
        }
    }
    std::string GetCurtime()
    {
        time_t tm = time(nullptr);
        struct tm curr;
        localtime_r(&tm, &curr);
        char buffer[1024];
        snprintf(buffer, sizeof(buffer), "%4d-%02d-%02d %02d:%02d:%02d",
                 curr.tm_year + 1900, curr.tm_mon, curr.tm_mday,
                 curr.tm_hour, curr.tm_min, curr.tm_sec);
        return std::string(buffer);
    }
    // Log负责日志的策略选择以及刷新
    class Log
    {
    public:
        Log()
        {
            UseConsoleStrategy();
        }
        void UseConsoleStrategy()
        {
            _ptr = std::make_unique<ScreenStrtegy>();
        }
        void UseFileStrategy()
        {
            _ptr = std::make_unique<FileLogStrategy>();
        }
        // 改类负责形成完整的语句
        class logmessgge
        {
        public:
            logmessgge(LogLevel &type, std::string filename, int line, Log &log)
                : _type(type),
                  _curr_time(GetCurtime()),
                  _pid(getpid()),
                  _filename(filename),
                  _line(line),
                  _log(log)

            {
                // 完成日志左边的写
                std::stringstream in;
                in
                    << "[" << _curr_time << "]" << " "
                    << "[" << LogLevelToString(_type) << "]"<<" "
                    << "[" << _pid << "]" << " "
                    << "[" << _filename << "]" << " "
                    << "[" << _line << "]" << " " << "-";
                _loginfo = in.str();
            }
            ~logmessgge()
            {
                // 实现自动调用我们的刷新逻辑
                // 因此我们要传入我们的外部类Log
                _log._ptr->SyncLog(_loginfo);
            }

            template <typename T>
            logmessgge &operator<<(const T &data)
            {
                // 支持重载
                std::stringstream in;
                in << data;
                _loginfo += in.str();
                return *this; // 为了支持连续进行输入
            }

        private:
            LogLevel _type;         // ⽇志等级
            std::string _curr_time; // ⽇志时间
            pid_t _pid;             //
            std::string _filename;  // 对应的⽂件名
            int _line;              // 对应的⽂件⾏号
            std::string _loginfo;
            Log &_log; // 外部类对象引用方便我们自动刷新
        };

        ~Log() {}
        // Log(level)<<"hellowore"
        // 我们想要如上面调用我们的日志进行我们的（）重载
        // 返回值写成拷贝返回
        // 临时对象这样我们进行日志写入会自动
        // 调用logmessge的析构函数进行刷新
        // 返回值为logmessage是为了调用我们的<<
        // 因此该函数就是支持把Log可以转化为logmessage类型

        //函数返回一个临时对象的引用时，
        //临时对象会在函数返回后立即被析构，导致返回的引用成为悬垂引用（Dangling Reference）
        //但是我们想要的是调用<<之后在结束因此
        //我们进行拷贝这样就延长了临时对象的生命周期
        //
        logmessgge operator()(LogLevel type, std::string filename, int line)
        {
            return logmessgge(type, filename, line, *this);
        }

    private:
        std::unique_ptr<LogStrategy> _ptr;
    };
    // 全局的日志对象
    Log mylog;
// 调用operator()
//具体就是先调用()去构造logmessage的信息接着调用>>完成对右边部分的填充接着析构调用刷新
#define LOG(level) mylog(level, __FILE__, __LINE__)

    // 提供选择使⽤何种⽇志策略的⽅法
#define ENABLE_CONSOLE_LOG_STRATEGY() mylog.UseConsoleStrategy()
#define ENABLE_FILE_LOG_STRATEGY() mylog.UseFileStrategy()
}